#pragma once
#include "cudaBase.cuh"
#include "Vector2.h"
#include "MagnetConstVariable.h"
#include <vector>

struct FieldPrecomputerConstant {

public:

	float volParticle;
	float induceFactor;
	float k;

	int size_x, size_y;
	float maxrange_x, maxrange_y;
	int max_x, max_y;
	
	float magnetPos_x, magnetPos_y;

	int scale;
};


class FieldPrecomputer {

private:

	float c_volParticle;
	float c_induceFactor;

	int m_size;
	Vector2 m_maxrange;
	int m_max_x, m_max_y;
	int m_scale;

	Vector2 m_pos;

	//float** m_Mfield;
	//float** m_Ffield;

	int m_magnet_num;
	Vector2* m_magnet_pos;
	Vector2* m_magnet_mm;

	float* dev_m_magnet_pos;
	float* dev_m_magnet_mm;

	float* dev_m_Mfield;
	float* dev_m_Ffield;


	FieldPrecomputerConstant data;

public:

	FieldPrecomputer(int numMagnet, Vector2 pos, int scale, Vector2 max_range, float volParticle) : 
		/*m_Mfield(NULL), m_Ffield(NULL),*/ m_magnet_pos(NULL), m_magnet_mm(NULL),
		dev_m_magnet_pos(NULL), dev_m_magnet_mm(NULL), dev_m_Mfield(NULL), dev_m_Ffield(NULL) {

			c_volParticle = volParticle;
			m_pos = pos;
			m_scale = scale;

			m_maxrange = max_range;
			m_max_x = (int)(m_maxrange.x() * m_scale);
			m_max_y = (int)(m_maxrange.y() * m_scale);

			m_magnet_num = numMagnet;
			m_magnet_pos = new Vector2[numMagnet];
			m_magnet_mm  = new Vector2[numMagnet];

			data.k = MagnetConst::k;
			data.induceFactor = c_induceFactor = 3 * 0.999f * MagnetConst::inverseU0 * volParticle;
			data.volParticle = volParticle;

			data.magnetPos_x = pos.x();
			data.magnetPos_y = pos.y();

			data.max_x = m_max_x;
			data.max_y = m_max_y;
			data.maxrange_x = m_maxrange.x();
			data.maxrange_y = m_maxrange.y();

			data.size_x = 2 * (int)m_max_x + 1;
			data.size_y = 2 * (int)m_max_y + 1;

			data.scale = scale;

			m_size = data.size_x * data.size_y;
			

			CUDA_MALLOC(dev_m_magnet_pos, sizeof(Vector2) * numMagnet);
			CUDA_MALLOC(dev_m_magnet_mm, sizeof(Vector2) * numMagnet);
			CUDA_MALLOC(dev_m_Mfield, sizeof(Vector2) * m_size);
			CUDA_MALLOC(dev_m_Ffield, sizeof(Vector2) * m_size);

	};
	~FieldPrecomputer() {
		if (dev_m_magnet_pos) CUDA_FREE(dev_m_magnet_pos);
		if (dev_m_magnet_mm) CUDA_FREE(dev_m_magnet_mm);
		if (dev_m_Ffield) CUDA_FREE(dev_m_Ffield);
		if (dev_m_Mfield) CUDA_FREE(dev_m_Mfield);

		if (m_magnet_pos) delete [] m_magnet_pos;
		if (m_magnet_mm) delete [] m_magnet_mm;
	};


	__forceinline void addMagnetCell(Vector2 pos, Vector2 mm, int idx) {
		m_magnet_pos[idx] = pos;
		m_magnet_mm[idx] = mm;
	}

	void SendMagnetToDevice() {
		CUDA_MEMCPY(dev_m_magnet_pos, m_magnet_pos, sizeof(Vector2) * m_magnet_num, cudaMemcpyHostToDevice);
		CUDA_MEMCPY(dev_m_magnet_mm, m_magnet_mm, sizeof(Vector2) * m_magnet_num, cudaMemcpyHostToDevice);
	}

	void SetMField();
	void SetFfield();

	__forceinline int GetSize() { return m_size; };


	void SendField(float* Mfield, float* Ffield) {
		CUDA_MEMCPY(Mfield, dev_m_Mfield, sizeof(Vector2) * m_size, cudaMemcpyDeviceToHost);
		CUDA_MEMCPY(Ffield, dev_m_Ffield, sizeof(Vector2) * m_size, cudaMemcpyDeviceToHost);
	};

	void SendMField(float *Mfield) {
		CUDA_MEMCPY(Mfield, dev_m_Mfield, sizeof(Vector2) * m_size, cudaMemcpyDeviceToHost);
	}

	void SendFField(float *Ffield) {
		CUDA_MEMCPY(Ffield, dev_m_Ffield, sizeof(Vector2) * m_size, cudaMemcpyDeviceToHost);
	}

};